//
// socket.cpp
// libprotea
// Tools Library for the Protea Project / Network Socket functionality
// Copyright (C) 2003, 2004, 2005 eXegeSys, Inc.
// Copyright (C) 2008 Bruce A. James
//
// This program is part of the Protea Project.
//
// The Protea Project is free software; you can redistribute it and/or modify it 
// under the terms of the GNU General Public License as published by the 
// Free Software Foundation, either version 3 of the License, or (at your 
// option) any later version.
//
// The Protea Project is distributed in the hope that it will be useful, but 
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
// for more details.
// 
// You should have received a copy of the GNU General Public License along 
// with this program.  If not, see <http://www.gnu.org/licenses/>.
// 
// Please send correspondence to:
// 
// theproteaproject@gmail.com
//

//
// Socket Transport
//
//	Defines the socket transport class
//
#include "../defines.h"
#include "socket.h"
#include "../AppHandler/AppInterface.h"

XCSocketTransport::XCSocketTransport( XCAppInterface* app_owner, XCMsgPort* port_owner ):
	XCTransport( app_owner, port_owner ) {

	INFUNC( XCSocketTransport::XCSocketTransport, myapp )
	mSocket = INVALID_SOCKET;
	OUTFUNC()
};

XCSocketTransport::XCSocketTransport( XCAppInterface* app_owner, SOCKET new_socket, XCMsgPort* port_owner ):
	XCTransport( app_owner, port_owner ) {

	INFUNC( XCSocketTransport::XCSocketTransport, myapp )
	mSocket = new_socket;
	OUTFUNC()
};

XCSocketTransport::~XCSocketTransport() {
	if (mSocket != INVALID_SOCKET)
		Close();
};

PORT_HANDLE XCSocketTransport::GetInHandle() {
	return (PORT_HANDLE)mSocket;
};

PORT_HANDLE XCSocketTransport::GetOutHandle() {
	return (PORT_HANDLE)mSocket;
};

int XCSocketTransport::SetBroadcast( bool broadcast ) {
	int on = broadcast ? 1 : 0;
	return setsockopt( mSocket, SOL_SOCKET, SO_BROADCAST, (const char*)&on, sizeof(on));
};

int XCSocketTransport::SetNagles( bool delay ) {
	int on = delay ? 0 : 1;
	return setsockopt( mSocket, IPPROTO_TCP, TCP_NODELAY, (const char*)&on, sizeof(on));
};

bool XCSocketTransport::GetNagles() {
	int on;

	socklen_t size = sizeof( on );

	getsockopt( mSocket,
		IPPROTO_TCP,
		TCP_NODELAY,
		(char*)&on,
		&size );
	return (on == 0);
};

void XCSocketTransport::Connect( const char* host, int port ) {
	INFUNC( XCSocketTransport::Connect, myapp );

	sockaddr_in rserver_address;
	hostent* rserver_host;

	rserver_host = gethostbyname( host );
	if ( rserver_host == NULL ) {
		if (myapp)
			myapp->ToolsTrace( TRC_TRAFFIC, TLV_HIGH, "Unknown host : %s", host );
		RAISETOOLSERROR( -1 );
	}

	memcpy( (char*) &rserver_address.sin_addr, rserver_host->h_addr, rserver_host->h_length );
	rserver_address.sin_family = rserver_host->h_addrtype;
	rserver_address.sin_port = htons( port );
	
	int result = connect( mSocket, (sockaddr*)&rserver_address, sizeof( rserver_address ) );
	if (result == SOCKET_ERROR)
		RAISEOSERROR();

	OUTFUNC();
};

bool XCSocketTransport::Listen( int backlog ) {
	return listen( mSocket, backlog ) == 0 ? true : false;
};

int XCSocketTransport::GetPort() {
	INFUNC( XCSocketTransport::GetPort, myapp )

	sockaddr_in server_address;

	int size = sizeof( server_address );

	if (getsockname( mSocket, (struct sockaddr*)&server_address, (socklen_t *)&size ) == -1)
		RAISETOOLSERROR( 5011 );

	OUTFUNCRET( htons(server_address.sin_port) )
};

void XCSocketTransport::Bind( int port ) {
	INFUNC( XCSocketTransport::Bind, myapp );

	sockaddr_in rserver_address;

	memset( &rserver_address, 0, sizeof( sockaddr_in ) );
	rserver_address.sin_addr.s_addr  = INADDR_ANY;
	rserver_address.sin_family = AF_INET;
	rserver_address.sin_port = htons( port );

	int result = bind( mSocket, (sockaddr*)&rserver_address, sizeof( sockaddr_in ) );
	if (result == -1) {
		char* reason = new char[ 256 ];
		sprintf( reason, "Socket error binding to port %d", port );
		try {
			RAISEOSERROR_PARAM( reason );
		} catch( ...) {
			// Just to cleanup the memory
			delete reason;
			throw;
		};
	};

	OUTFUNC();
};

void XCSetupAddr( char* host, int port, sockaddr_in& addr ) {
	hostent* rserver_host = gethostbyname( host );

	memcpy( (char*) &addr.sin_addr, rserver_host->h_addr, rserver_host->h_length );
	addr.sin_family = rserver_host->h_addrtype;
	addr.sin_port = htons( (u_short)port );
};

int XCGetHostName( char *name, int len )
{
	return gethostname( name, len );
};
